package command

import (
	"bufio"
	"fmt"
	"os"
	"path"
	"path/filepath"
	"strings"
	"text/template"

	"github.com/spf13/cobra"

	"github.com/pyroscope-io/pyroscope/pkg/cli"
	"github.com/pyroscope-io/pyroscope/pkg/config"
)

func newCiCmd(cfg *config.CI) *cobra.Command {
	vpr := newViper()

	var cmd *cobra.Command
	cmd = &cobra.Command{
		Use:    "ci",
		Short:  "",
		Hidden: true,
		RunE: cli.CreateCmdRunFn(cfg, vpr, func(_ *cobra.Command, _ []string) error {
			fmt.Println(cfg)
			printUsageMessage(cmd)
			return nil
		}),
	}

	cli.PopulateFlagSet(cfg, cmd.Flags(), vpr)
	cmd.AddCommand(newCiInstallCmd(cfg))

	return cmd
}

// admin app
func newCiInstallCmd(cfg *config.CI) *cobra.Command {
	vpr := newViper()

	var cmd *cobra.Command
	cmd = &cobra.Command{
		Use:    "install",
		Short:  "",
		Hidden: true,
		RunE: cli.CreateCmdRunFn(cfg, vpr, func(_ *cobra.Command, arg []string) error {
			// TODO: support passing custom paths
			if len(arg) != 1 {
				printUsageMessage(cmd)
				return nil
			}

			path := arg[0]
			return install(path, cfg.ApplicationName)
		}),
	}
	cli.PopulateFlagSet(cfg, cmd.Flags(), vpr)

	return cmd
}

func isTestFile(p string) bool {
	// TODO: is this enough?
	return strings.HasSuffix(p, "_test.go")
}

func install(basePath string, appName string) error {
	type TestPackage struct {
		Path        string
		PackageName string
	}

	// Use a map to not have any duplicates
	testPackages := make(map[string]TestPackage)

	// Find all test files
	err := filepath.Walk(basePath,
		func(p string, info os.FileInfo, err error) error {
			if err != nil {
				return err
			}
			if info.IsDir() {
				return nil
			}

			if isTestFile(p) {
				dirName, _ := filepath.Split(p)
				packageName := filepath.Base(dirName)

				testPackages[dirName] = TestPackage{
					Path:        dirName,
					PackageName: packageName + "_test",
				}
			}

			return nil
		})
	if err != nil {
		return err
	}

	t1 := template.New("t1")
	t1, err = t1.Parse(`
// Code generated by pyroscope.
package {{.PackageName }}

import (
	"github.com/pyroscope-io/client/pyroscope"
)

func init() {
	pyroscope.Start(pyroscope.Config{
		ProfileTypes:    []pyroscope.ProfileType{pyroscope.ProfileCPU},
		ApplicationName: "{{ .ApplicationName }}",
	})
}
`)
	if err != nil {
		return err
	}

	// For each package
	// Generate a pyroscope_test.go file
	for _, v := range testPackages {
		testFile := path.Join(v.Path, "pyroscope_test.go")
		output, err := os.Create(testFile)
		if err != nil {
			return err
		}
		w := bufio.NewWriter(output)

		err = t1.Execute(w, struct {
			PackageName     string
			ApplicationName string
		}{
			PackageName:     v.PackageName,
			ApplicationName: appName,
		})

		if err != nil {
			return err
		}

		if err = w.Flush(); err != nil {
			return err
		}

		fmt.Println("Created file", testFile)
	}

	return nil
}
